package org.kgduyjj.toolkit.util;

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;

import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

/**
 * 对象工具类
 *
 * @author Kgduyjj
 * @since 2020-12-18
 * @version 1.0.0
 */
public class ObjectUtils {
    /**
     * 获取参数不为空值
     * 
     * @param value defaultValue 要判断的value
     * @return value 返回值
     */
    public static <T> T nvl(T value, T defaultValue) {
        return ObjectUtils.notNull(value) ? value : defaultValue;
    }

    /**
     * 判断只要有一个为空，则返回<code>True</code>
     * 
     * @param objects
     * @return
     */
    public static boolean anyNull(Object... objects) {
        for (Object object : objects) {
            if (isNull(object)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断传入对象是否为空
     * <p>
     * 这个方法支持的对象类型
     * <ul>
     * <li>{@code Optional}: 如果认为是空的 {@link Optional#empty()}</li>
     * <li>{@link CharSequence}: 如果字符序列长度为0，则视为空</li>
     * <li>{@link Array} : 如果数组为空或者长度为0，则视为空</li>
     * <li>{@link Collection}: 委托给{@link Collection#isEmpty()}进行判定</li>
     * <li>{@link Map}: 委托给 {@link Map#isEmpty()}进行判定</li>
     * </ul>
     * <p>
     * 如果给定的对象为非空且不支持上述对象之一类型，此方法返回{@code false}。
     * 
     * @param 被检查对象
     * @return {@code true} if the object is {@code null} or <em>empty</em>
     * @see Optional#isPresent()
     * @see CharSequence#length()
     * @see ObjectUtils#isEmptyArray(Object...)
     * @see StringUtils#hasLength(CharSequence)
     * @see CollectionUtils#isEmpty(java.util.Collection)
     * @see CollectionUtils#isEmpty(java.util.Map)
     */
    public static boolean isNull(@Nullable Object obj) {
        if (obj == null) {
            return true;
        }
        if (obj instanceof Optional) {
            return !((Optional<?>) obj).isPresent();
        }
        if (obj instanceof CharSequence) {
            return ((CharSequence) obj).length() == 0;
        }
        if (obj instanceof Array) {
            return isNullArray(obj);
        }
        if (obj instanceof Collection) {
            return ((Collection<?>) obj).isEmpty();
        }
        if (obj instanceof Map) {
            return ((Map<?, ?>) obj).isEmpty();
        }
        return false;
    }

    public static boolean isNullArray(@Nullable Object... array) {
        if (array == null || array.length == 0) {
            return true;
        }
        return false;
    }

    /**
     * 判断元素不为空
     * 
     * @param obj object
     * @return
     */
    public static boolean notNull(@Nullable Object obj) {
        return !isNull(obj);
    }

    /**
     * 对象组中是否存在 空对象
     *
     * @param objs 对象组
     * @return boolean
     */
    public static boolean hasNull(Object... objs) {
        for (Object obj : objs) {
            if (isNull(obj)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 确定给定的对象是否相等，如果两者都为null，则返回true；如果只有一个为null，则返回false。将数组与数组.equals，根据数组元素而不是数组引用执行相等检查。
     * 
     * @param o1 first Object to compare
     * @param o2 second Object to compare
     * @return whether the given objects are equal
     * @see Object#equals(Object)
     * @see java.util.Arrays#equals
     */
    public static boolean nullSafeEquals(@Nullable Object o1, @Nullable Object o2) {
        if (o1 == o2) {
            return true;
        }
        if (o1 == null || o2 == null) {
            return false;
        }
        if (o1.equals(o2)) {
            return true;
        }
        if (o1.getClass().isArray() && o2.getClass().isArray()) {
            return arrayEquals(o1, o2);
        }
        return false;
    }

    /**
     * 将给定数组与数组.equals，根据数组元素而不是数组引用执行equalitycheck。
     * 
     * @param o1 first array to compare
     * @param o2 second array to compare
     * @return whether the given objects are equal
     * @see #nullSafeEquals(Object, Object)
     * @see java.util.Arrays#equals
     */
    private static boolean arrayEquals(Object o1, Object o2) {
        if (o1 instanceof Object[] && o2 instanceof Object[]) {
            return Arrays.equals((Object[]) o1, (Object[]) o2);
        }
        if (o1 instanceof boolean[] && o2 instanceof boolean[]) {
            return Arrays.equals((boolean[]) o1, (boolean[]) o2);
        }
        if (o1 instanceof byte[] && o2 instanceof byte[]) {
            return Arrays.equals((byte[]) o1, (byte[]) o2);
        }
        if (o1 instanceof char[] && o2 instanceof char[]) {
            return Arrays.equals((char[]) o1, (char[]) o2);
        }
        if (o1 instanceof double[] && o2 instanceof double[]) {
            return Arrays.equals((double[]) o1, (double[]) o2);
        }
        if (o1 instanceof float[] && o2 instanceof float[]) {
            return Arrays.equals((float[]) o1, (float[]) o2);
        }
        if (o1 instanceof int[] && o2 instanceof int[]) {
            return Arrays.equals((int[]) o1, (int[]) o2);
        }
        if (o1 instanceof long[] && o2 instanceof long[]) {
            return Arrays.equals((long[]) o1, (long[]) o2);
        }
        if (o1 instanceof short[] && o2 instanceof short[]) {
            return Arrays.equals((short[]) o1, (short[]) o2);
        }
        return false;
    }

}
